import * as d3 from '../../../static/js/d3.v4.min'
function JusTree3(params) {
  this.width = params.width ? params.width : document.querySelector(params.divId).offsetWidth
  this.height = params.height ? params.height : document.querySelector(params.divId).offsetHeight
  this.divId = params.divId
  this.data = params.data || []
}
JusTree3.prototype = {
  init: function() {
    this.drawChart()
  },
  drawChart: function() {
    const initScale = 0.6
    let draging = false
    // 主体  255 159 0
    // 人    158 234 106
    // 公司  132 165 239
    const nodeConf = {
      fillColor: {
        Human: 'rgb(158, 234, 106)',
        Company: 'rgb(132, 165, 239)',
        Risk: 'rgb(255, 0, 0)'
      },
      strokeColor: {
        Human: 'rgb(158, 234, 106)',
        Company: 'rgb(35, 148, 206)',
        Risk: 'rgb(255, 0, 0)'
      },
      strokeWidth: {
        Human: 3,
        Company: 0,
        Risk: 0
      },
      textFillColor: {
        Human: '#fff',
        Company: '#fff',
        Risk: '#fff'
      },
      radius: {
        Human: 36,
        Company: 54,
        Risk: 54
      }
    }
    const lineConf = {
      strokeColor: {
        // 关系
        SERVE: 'rgb(128, 194, 216)',
        OWN: 'rgb(204, 225, 152)',
        INVEST_C: 'rgb(242, 90, 41)'
      }
    }
    const nodeTextFontSize = 18
    const lineTextFontSize = 16
    let nodesMap = {}
    let linkMap = {}
    let mainid = ''
    // 力导向图
    const force = d3.layout.force()
      .size([this.width, this.height]) // 画布的大小
      .linkDistance(400) // 连线长度
      .charge(-2000) // 排斥/吸引，值越小越排斥
    // 全图缩放器
    const zoom = d3.behavior.zoom()
      .scaleExtent([0.25, 2])
      .on('zoom', zoomFn)
    // 节点拖拽器（使用 d3.behavior.drag 节点拖动失效）
    const drag = force.drag()
      .origin(d => d)
      .on('dragstart', dragstartFn)
      .on('drag', dragFn)
      .on('dragend', dragendFn)
    // SVG
    const svg = d3.select(this.divId).append('svg')
      .attr('width', this.width)
      .attr('height', this.height)
      .append('g')
      .call(zoom)
      .on('dblclick.zoom', null)
    // 缩放层（位置必须在 container 之前）
    svg.append('rect')
      .attr('width', this.width)
      .attr('height', this.height)
      .style('fill', 'none')
      .style('pointer-events', 'all')
    const container = svg.append('g')
      .attr('transform', 'scale(' + initScale + ')')
      .attr('class', 'container')
    // 初始化
    // setTimeout(function() {
    initialize(this.data)
    // }, 10);
    // 初始化
    // let boxwidth = this.width
    // let boxheight = this.height
    function initialize(resp) {
      let {
        nodes,
        relations,
        main
      } = resp
      mainid = main.id
      // 生成 nodes map
      nodesMap = genNodesMap(nodes)
      // 构建 nodes（不能直接使用 api 中的 nodes）
      nodes = d3.values(nodesMap)
      // 起点和终点相同的关系映射
      linkMap = genLinkMap(relations)
      // 构建 links（source 属性必须从 0 开始）
      const links = genLinks(relations)
      // 绑定力导向图数据
      force
        .nodes(nodes) // 设定节点数组
        .links(links) // 设定连线数组
      // 开启力导向布局
      force.start()
      // 手动快速布局
      for (let i = 0, n = 1000; i < n; ++i) {
        force.tick()
      }
      // 停止力布局
      force.stop()
      // 固定所有节点
      nodes.forEach(node => {
        node.fixed = true
      })
      // 箭头
      // const marker =
      container.append('svg:defs').selectAll('marker')
        .data(force.links())
        .enter().append('svg:marker')
        .attr('id', link => 'marker-' + link.id)
        .attr('markerUnits', 'userSpaceOnUse')
        .attr('viewBox', '0 -5 10 10')
        // .attr('viewBox', `0 0 ${boxwidth} ${boxheight}`)
        .attr('refX', nodeConf.radius.Company)
        .attr('refY', 0)
        .attr('markerWidth', 12)
        .attr('markerHeight', 12)
        .attr('orient', 'auto')
        .attr('stroke-width', 2)
        .append('svg:path')
        .attr('d', 'M2,0 L0,-3 L9,0 L0,3 M2,0 L0,-3')
        .attr('fill', link => lineConf.strokeColor[link.type])
      // 节点连线
      const linkLine = container.selectAll('.link')
        .data(force.links())
        .enter()
        .append('path')
        .attr('class', 'link')
        .attr({
          'marker-end': link => 'url(#' + 'marker-' + link.id + ')', // 标记箭头
          'd': link => genLinkPath(link),
          'id': link => 'link-' + link.id
        })
        .style('stroke', link => lineConf.strokeColor[link.type])
      // 连线的文字
      const lineText = container.append('g').selectAll('.linetext')
        .data(force.links())
        .enter()
        .append('text')
        .style('font-size', lineTextFontSize)
        .attr({
          'class': 'linetext',
          'id': link => 'linktext' + link.id,
          'dx': link => getLineTextDx(link),
          'dy': 5
        })
      lineText.append('textPath')
        .attr('xlink:href', link => '#link-' + link.id)
        .text(link => link.labels)
      // 节点（圆）
      const nodeCircle = container.append('g')
        .selectAll('.node')
        .data(force.nodes())
        .enter()
        .append('g')
        .style('cursor', 'pointer')
        .attr('class', 'node')
        .attr('cx', node => node.x)
        .attr('cy', node => node.y)
        .call(drag) // 节点可拖动
      nodeCircle.append('circle')
        .style('fill', function(node) {
          if (node.id === mainid) {
            return 'rgb(255, 159, 0)'
          } else {
            return nodeConf.fillColor[node.ntype]
          }
        })
        .style('stroke', node => nodeConf.strokeColor[node.ntype])
        .style('stroke-width', node => nodeConf.strokeWidth[node.ntype])
        .attr('class', 'node-circle')
        .attr('id', node => 'node-circle-' + node.id)
        .attr('r', function(node) {
          if (node.id === mainid) {
            return 62
          } else {
            return nodeConf.radius[node.ntype]
          }
        })
        .style('opacity', function(d) {
          if (d.id === mainid) {
            // 初始化隐藏其他连接线上的文字
            lineText.style("fill-opacity", function(edge) {
              if (edge.source.id === d.id || edge.target.id === d.id) {
                return 1
              }
              if (edge.source.id !== d.id && edge.target.id !== d.id) {
                return 0.2
              }
            })
            // 初始化隐藏其他连线
            linkLine.style("opacity", function(edge) {
              if (edge.source.id === d.id || edge.target.id === d.id) {
                return 1
              }
              if (edge.source.id !== d.id && edge.target.id !== d.id) {
                return 0.2
              }
            })
          }
          if (d.id === mainid || d.list[mainid] === mainid) {
            return 1
          } else {
            return 0.7
          }
        })
        // 鼠标交互
      // 节点文字
      const nodeText = nodeCircle.append('text')
        .attr('class', 'nodetext')
        .attr('id', node => 'node-text-' + node.id)
        .style('font-size', nodeTextFontSize)
        .style('font-weight', 400)
        .style('fill', ({
          ntype
        }) => nodeConf.textFillColor[ntype])
        .attr('text-anchor', 'middle')
        .attr('dy', '.35em')
        .attr('x', function({
          name
        }) {
          return textBreaking(d3.select(this), name)
        })
        .style('opacity', function(d) {
          if (d.id === mainid || d.list[mainid] === mainid) {
            return 1
          } else {
            return 0.7
          }
        })
        .on('dblclick', function(d) {
          if (draging) {
            return
          }
          if (d.ntype !== 'Human' && d.name !== main) {
            let detailHref =
              'http://' +
              location.host +
              '#/entdetail/baseinfo?entname=' +
              d.name
            window.open(detailHref)
          }
        })
      nodeCircle.on('click', function(currNode) {
        d3.event.stopPropagation()
        if (d3.event.defaultPrevented) return
        // if (draging) {
        //   return
        // }
        // toggleNode(nodeCircle, currNode, true)
        // toggleLine(linkLine, currNode, true)
        // toggleMarker(marker, currNode, true)
        // toggleLineText(lineText, currNode, true)
        nalchange(currNode)
      })
      function nalchange(d) {
        // 隐藏其它连线上文字
        lineText.style("fill-opacity", function(edge) {
          if (edge.source.id === d.id || edge.target.id === d.id) {
            return 1
          }
          if (edge.source.id !== d.id && edge.target.id !== d.id) {
            return 0.2
          }
        })
        // 隐藏其它连线
        linkLine.style("opacity", function(edge) {
          if (edge.source.id === d.id || edge.target.id === d.id) {
            return 1
          }
          if (edge.source.id !== d.id && edge.target.id !== d.id) {
            return 0.2
          }
        })
        // 其它节点亮度调低
        nodeCircle.style("opacity", function(edge) {
          let v = d.id
          if (edge.id === v || edge.list[d.id] === v) {
            return 1
          } else {
            return 0.2
          }
        })
        // 隐藏其它节点文字
        nodeText.style("opacity", function(edge) {
          let v = d.id
          if (edge.id === v || edge.list[d.id] === v) {
            return 1
          } else {
            return 0.2
          }
        })
      }
      // 更新力导向图
      function tick() {
        // 节点位置
        nodeCircle.attr('transform', node => 'translate(' + node.x + ',' + node.y + ')')
        // 连线路径
        linkLine.attr('d', link => genLinkPath(link))
        // 连线文字位置
        lineText.attr('dx', link => getLineTextDx(link))
        // 连线文字角度
        lineText.attr('transform', function(link) {
          return getLineTextAngle(link, this.getBBox())
        })
      }
      // 更新力导向图
      // 注意1：必须调用一次 tick （否则，节点会堆积在左上角）
      // 注意2：调用位置必须在 nodeCircle, nodeText, linkLine, lineText 后
      tick()
      // 监听力学图运动事件，更新坐标
      force.on('tick', tick)
    }
    function genLinks(relations) {
      const indexHash = {}
      return relations.map(function({
        id,
        startNode,
        endNode,
        label,
        type
      }, i) {
        const linkKey = startNode + '-' + endNode
        const count = linkMap[linkKey]
        if (indexHash[linkKey]) {
          indexHash[linkKey] -= 1
        } else {
          indexHash[linkKey] = count - 1
        }
        return {
          id,
          source: nodesMap[startNode],
          target: nodesMap[endNode],
          label,
          type,
          labels: linkMap[linkKey + '-label'],
          count: linkMap[linkKey],
          index: indexHash[linkKey]
        }
      })
    }
    function genLinkMap(relations) {
      const hash = {}
      relations.map(function({
        startNode,
        endNode,
        label
      }) {
        const key = startNode + '-' + endNode
        if (hash[key]) {
          hash[key] += 1
          hash[key + '-label'] += '、' + label
        } else {
          hash[key] = 1
          hash[key + '-label'] = label
        }
      })
      return hash
    }
    function genNodesMap(nodes) {
      const hash = {}
      nodes.map(function({
        id,
        name,
        ntype,
        list
      }) {
        hash[id] = {
          id,
          name,
          ntype,
          list
        }
      })
      return hash
    }
    // 生成关系连线路径
    function genLinkPath(link) {
      let sx = link.source.x
      let tx = link.target.x
      let sy = link.source.y
      let ty = link.target.y
      return 'M' + sx + ',' + sy + ' L' + tx + ',' + ty
    }
    function zoomFn() {
      const {
        translate,
        scale
      } = d3.event
      container.attr('transform', 'translate(' + translate + ')scale(' + scale * initScale + ')')
    }
    function dragstartFn(d) {
      draging = true
      d3.event.sourceEvent.stopPropagation()
      force.start()
    }
    function dragFn(d) {
      draging = true
      d3.select(this)
        .attr('cx', d.x = d3.event.x)
        .attr('cy', d.y = d3.event.y)
    }
    function dragendFn(d) {
      draging = false
      force.stop()
    }
    // function isLinkLine(node, link) {
    //   return link.source.id === node.id || link.target.id === node.id
    // }
    // function isLinkNode(currNode, node) {
    //   if (currNode.id === node.id) {
    //     return true
    //   }
    //   return linkMap[currNode.id + '-' + node.id] || linkMap[node.id + '-' + currNode.id]
    // }
    function textBreaking(d3text, text) {
      const len = text.length
      if (len <= 4) {
        d3text.append('tspan')
          .attr('x', 0)
          .attr('y', 2)
          .text(text)
      } else {
        const topText = text.substring(0, 4)
        const midText = text.substring(4, 9)
        let botText = text.substring(9, len)
        let topY = -22
        let midY = 8
        let botY = 34
        if (len <= 10) {
          topY += 10
          midY += 10
        } else {
          botText = text.substring(9, 11) + '...'
        }
        d3text.text('')
        d3text.append('tspan')
          .attr('x', 0)
          .attr('y', topY)
          .text(function() {
            return topText
          })
        d3text.append('tspan')
          .attr('x', 0)
          .attr('y', midY)
          .text(function() {
            return midText
          })
        d3text.append('tspan')
          .attr('x', 0)
          .attr('y', botY)
          .text(function() {
            return botText
          })
      }
    }
    function getLineTextDx(d) {
      const sr = nodeConf.radius[d.source.ntype]
      const sx = d.source.x
      const sy = d.source.y
      const tx = d.target.x
      const ty = d.target.y
      const distance = Math.sqrt(Math.pow(tx - sx, 2) + Math.pow(ty - sy, 2))
      const textLength = d.labels.length
      const deviation = 8 // 调整误差
      const dx = (distance - sr - textLength * lineTextFontSize) / 2 + deviation
      return dx
    }
    function getLineTextAngle(d, bbox) {
      if (d.target.x < d.source.x) {
        const {
          x,
          y,
          width,
          height
        } = bbox
        const rx = x + width / 2
        const ry = y + height / 2
        return 'rotate(180 ' + rx + ' ' + ry + ')'
      } else {
        return 'rotate(0)'
      }
    }
    // function toggleNode(nodeCircle, currNode, isHover) {
    //   if (isHover) {
    //     // 提升节点层级
    //     nodeCircle.sort((a, b) => a.id === currNode.id ? 1 : -1)
    //     nodeCircle
    //       .style('opacity', 0.2)
    //       .filter(node => isLinkNode(currNode, node))
    //       .style('opacity', 1)
    //   } else {
    //     nodeCircle.style('opacity', 1)
    //   }
    // }
    // function toggleLine(linkLine, currNode, isHover) {
    //   if (isHover) {
    //     // 加重连线样式
    //     linkLine
    //       .style('opacity', 0.2)
    //       .filter(link => isLinkLine(currNode, link))
    //       .style('opacity', 1)
    //       .classed('link-active', true)
    //   } else {
    //     // 连线恢复样式
    //     linkLine
    //       .style('opacity', 1)
    //       .classed('link-active', false)
    //   }
    // }
    // function toggleLineText(lineText, currNode, isHover) {
    //   if (isHover) {
    //     // 只显示相连连线文字
    //     lineText
    //       .style('fill-opacity', link => isLinkLine(currNode, link) ? 1.0 : 0.0)
    //   } else {
    //     // 显示所有连线文字
    //     lineText
    //       .style('fill-opacity', '1.0')
    //   }
    // }
    // function toggleMarker(marker, currNode, isHover) {
    //   if (isHover) {
    //     // 放大箭头
    //     marker.filter(link => isLinkLine(currNode, link))
    //       .style('transform', 'scale(1.5)')
    //   } else {
    //     // 恢复箭头
    //     marker
    //       .attr('refX', nodeConf.radius.Company)
    //       .style('transform', 'scale(1)')
    //   }
    // }
  }
}
export default JusTree3



// WEBPACK FOOTER //
// ./src/assets/js/jus_tree3.js